#if !defined( __state_bkjlsdoi7fjwe90e3wf9__ )
#define __state_bkjlsdoi7fjwe90e3wf9__

#include <cstdint>
#include <vector>
#include <tuple>
#include <functional>
#include <mutex>
#include <atomic>
#include <condition_variable>

#include <hude/base/base_type.hpp>
#include <hude/base/log.hpp>

namespace hude {
	/**********************************************************************************************/
	namespace utils
	{
        /***********************************************************************************************
        * @brief 状态的基类定义
        */
		template< typename _machine_t > struct state_basic_t
		{
			typedef _machine_t                     machine_t;
			typedef typename _machine_t::user_t    user_t;
			typedef typename _machine_t::id_t      id_t;
			typedef state_basic_t< machine_t >     this_t;

			virtual ~state_basic_t() {}

			virtual id_t getid() const { return id_t(); }
			virtual void enter( machine_t*, this_t*, user_t ) {}
			virtual	void leave( machine_t*, this_t*, user_t ) {}
		};

        /***********************************************************************************************
        * @brief 通用状态机定义
        * @param _STATE_ID: 状态机的ID类型
        * @param _USER: 用户数据类型，状态机在转换状态时传递
        */
		template< typename _STATE_ID, typename _USER = uintptr_t >
		class state_machine_t
		{
		public:
			/***********************************************************************************************
			* @brief 状态机的ID类型
			*/
			typedef _STATE_ID id_t;

			/***********************************************************************************************
			* @brief 用户数据类型
			*/
			typedef _USER     user_t;

			/***********************************************************************************************
			* @brief this_t
			*/
			typedef state_machine_t< _STATE_ID, _USER > this_t;
		
			/***********************************************************************************************
			* @brief 离开或进入状态事件类型
			*/
			enum cb_type_t
			{
				emCHANGE_LEAVE,
				emCHANGE_ENTER,
			
				emCHANGE_MAX
			};

			/***********************************************************************************************
			* @brief 状态类型
			*/
			typedef state_basic_t< this_t > state_t;

			/***********************************************************************************************
			* @brief 状态变化的回调类型
			*/
			typedef std::function< void( cb_type_t, state_t*, state_t* ) > cb_change_t;

			/***********************************************************************************************
			* @brief 内部实现使用的类型
			*/
			typedef std::mutex mutex_t;
			typedef std::unique_lock< mutex_t > autolock_t;
			typedef std::atomic< bool > running_t;

			struct next_t { state_t* state_; user_t user_; };
		
		public:
			/***********************************************************************************************
			* @brief 构造函数
			* @param cb_change: 状态变化时的事件回调
			*/
			state_machine_t( cb_change_t cb_change = cb_change_t() ) :
				cb_change_( cb_change ), current_( nullptr ), next_( next_t() ), running_( false )
			{
			}

			/***********************************************************************************************
			* @brief 状态工作循环，该函数启动状态机并将阻塞调用者
			* @param first: 可提供一个初始状态
			* @param user: 为初始状态传递的用户数据
			*/
			void loop( state_t* first, user_t user = user_t() );

			/***********************************************************************************************
			* @brief 停止状态工作循环
			*/
			void stop();

			/***********************************************************************************************
			* @brief 切换到指定的状态
			* @param state: 指定要切换的状态
			* @param user: 指定要传递的用户数据
			*/
			void next( state_t* state, user_t user = user_t() );

			/***********************************************************************************************
			* @brief 查询当前的状态
			* @return 返回当前的状态
			*/
			const state_t* current() const { return current_; }

			/***********************************************************************************************
			* @brief 获取锁定状态的互斥量，外面可以持有该互斥量以锁定当前状态，以阻塞任何状态切换
			* @return 返回可锁定状态的互斥量
			*/
			mutex_t& hold() { return chaning_; }

			/***********************************************************************************************
			* @brief 查询状态机当前是否运行中
			* @return 返回原子的运行标记
			*/
			const running_t& running() const { return running_; }

		private:
			void change( state_t*, user_t = user_t() );

		private:
			cb_change_t cb_change_;
			state_t *current_;
			next_t next_;
			mutex_t  mutex_, chaning_;
			std::condition_variable cv_;
			running_t running_;
		
		};

		/*********************************************************************************************/
		template< typename _STATE_ID, typename _USER >
		inline void state_machine_t< _STATE_ID, _USER >::loop( state_t* first, user_t user )
		{
			next_ = next_t{ first, user };
			running_ = true;

			next_t next;

			while ( running_ )
			{
				{
					autolock_t _l( mutex_ );

					cv_.wait( _l, [ this ] { return running_ == false || next_.state_ != nullptr; } );

					// when running_ == false
					if ( next_.state_ == nullptr ) { continue; }

					next = next_;

					next_ = next_t{ nullptr, user_t() };
				}

				this->change( next.state_, next.user_ );
			}
		}

		/*********************************************************************************************/
		template< typename _STATE_ID, typename _USER >
		inline void state_machine_t< _STATE_ID, _USER >::stop()
		{
			{
				autolock_t _l( mutex_ );

				running_ = false;
			}

			cv_.notify_one();
		}

		/*********************************************************************************************/
		template< typename _STATE_ID, typename _USER >
		inline void state_machine_t< _STATE_ID, _USER >::next( state_t* state, user_t user )
		{
			{
				autolock_t _l( mutex_ );

				next_ = next_t{ state, user };
			}

			cv_.notify_one();
		}

		/*********************************************************************************************/
		template< typename _STATE_ID, typename _USER >
		inline void state_machine_t< _STATE_ID, _USER >::change( state_t* enter, user_t user )
		{
			autolock_t _c( chaning_ );

			state_t* leave = current_;

			cb_change_( emCHANGE_LEAVE, leave, enter );

			if( leave != nullptr ) leave->leave( this, enter, user );

			current_ = enter;

			if( enter != nullptr ) enter->enter( this, leave, user );
		
			cb_change_( emCHANGE_ENTER,  enter, leave );
		}

	} //namespace utils

} //namespace hude

#endif //__state_bkjlsdoi7fjwe90e3wf9__
//file end
